#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od; 
  else if (this->operateur=="ou") valeur = og || od;
  else if (this->operateur=="et") valeur = og && od;
  else if (this->operateur=="<") valeur = og < od;
  else if (this->operateur==">") valeur = og > od;
  else if (this->operateur=="<=") valeur = og <= od;
  else if (this->operateur==">=") valeur = og >=od;
  else if (this->operateur=="==" || this->operateur=="non") valeur = og == od;
  else if (this->operateur=="!=") valeur = og != od;

  else  // this->operateur=="/"
	if (od!=0)
		valeur = og/od;
  	else {
	  	cout << "Erreur pendant l'interpretation : division par zero" << endl;
	  	exit(0); // plus tard on levera une exception
  	}
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////
NoeudInstSi::NoeudInstSi(vector<NoeudInstCond*> tabInstSi, Noeud* instSinon = NULL) {
    this->m_tabInstSi = tabInstSi;
    this->m_instSinon = instSinon;
    
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstSi::getValeur() {
  
  for (unsigned int i=0; i<m_tabInstSi.size(); i++) {
    if (m_tabInstSi[i]->getCond()) return m_tabInstSi[i]->getValeur(); //si la condition est vrai alors on retourne la valeur de sa sequence
  }
  if (m_instSinon != NULL) //m_instSinon est NULL si il n y a pas de sinon
  return this->m_instSinon->getValeur(); //si il  ya un sinon, on  retourne la valeur de sa sequence 
  
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::afficher(unsigned short indentation) {
  unsigned int i=1;
  
  Noeud::afficher(indentation);
  cout << "Noeud - InstSi" << endl;
  m_tabInstSi[0]->afficher(indentation);
  if (m_tabInstSi.size() > 0) {
      while ( i < m_tabInstSi.size()) {
        Noeud::afficher(indentation); 
        cout << "Noeud - InstSinonSi" << endl;  
        m_tabInstSi[i]->afficher(indentation); 
        i++;
      }
  }
  if (m_instSinon != NULL) {
      Noeud::afficher(indentation);
      cout<< "Noeud - InstSinon" << endl;
      m_instSinon->afficher(indentation+1);
  }
}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstCond
////////////////////////////////////////////////////////////////////////////////
NoeudInstCond::NoeudInstCond(Noeud* cond, Noeud* inst) {
    this->m_cond = cond;
    this->m_inst = inst;
    
}
////////////////////////////////////////////////////////////////////////////////
bool NoeudInstCond::getCond() {
    //getCond retourne le resultat de l'expression booléenne de l'instruction
    if (m_cond->getValeur()==1) return 1;
    else return 0;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstCond::getValeur() {
    //getValeur retourne le resultat de la séquence de l'instruction

    return m_inst->getValeur();
    

}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstCond::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    m_cond->afficher(indentation+1);
    m_inst->afficher(indentation+1);
    
}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* cond, Noeud* inst) : NoeudInstCond::NoeudInstCond(cond,inst){
    
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstTq::getValeur() {
    int valeur = 0;
    while (NoeudInstCond::getCond()) {
        valeur = NoeudInstCond::getValeur();
    }
    return valeur;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstTq::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << " Noeud - InstTq" << endl;
    NoeudInstCond::afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////
NoeudInstRepeter::NoeudInstRepeter(Noeud* cond, Noeud* inst) : NoeudInstCond::NoeudInstCond(cond,inst){
    
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstRepeter::getValeur() {
    int valeur = 0;
    while (!NoeudInstCond::getCond()) {
        valeur = NoeudInstCond::getValeur();
    }
    return valeur;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstRepeter::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << " Noeud - InstRepeter" << endl;
    NoeudInstCond::afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* ini, Noeud* cond, Noeud* inc, Noeud* seq) {
    m_ini = ini;
    m_cond = cond;
    m_inc = inc;
    m_seq = seq;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstPour::getValeur() {
    int valeur = 0;

    for(m_ini->getValeur();m_cond->getValeur();m_inc->getValeur()) {
        
        valeur = m_seq->getValeur();
    }
    
    return valeur;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout<< "Noeud - InstPour" << endl;
    m_ini->afficher(indentation+1);
    m_cond->afficher(indentation+1);
    m_inc->afficher(indentation+1);
    m_seq->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* var, int val) {
    m_var=var;
    m_val=val;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstLire::getValeur() {
  ((SymboleValue*)m_var)->setValeur(m_val); // on affecte la variable
  return m_val; // par convention, une affectation a pour valeur la valeur affectée
    
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout<< "Noeud - InstLire" << endl;
    m_var->afficher(indentation+1);
}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////
NoeudInstEcrire::NoeudInstEcrire(Noeud* exp) {
    m_exp = exp;
 
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstEcrire::getValeur() {
    return m_exp->getValeur();
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout<<"Noeud - InstEcrire" << endl;
    m_exp->afficher(indentation+1);
}